﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MoneyWentWhere.Utils;
using MoneyWentWhere.OutputHelpers;

namespace MoneyWentWhere.SimpleTextFile.ExchangeRates
{
    public class ExchangeRateTable
    {
        // Mapping (FromCurrency, ToCurrency) to echange rate list sorted by date, increasing
        ILookup<Tuple<string, string>, ExchangeRate> _table;

        // Strategy pattern
        public IExchangeRateSource Source;

        public ExchangeRateTable(IExchangeRateSource source)
        {
            if (source == null) { throw new ArgumentNullException("source"); }
            Source = source;

            var rates = Source.GetExchangeRates();
            _table = ToLookupTable(rates);
        }

        /// <summary>
        /// Get the exchange rate for appropriate currencies, on or before the given date.
        /// Returns null if no rate is present.
        /// </summary>
        /// <param name="fromCurrency"></param>
        /// <param name="toCurrency"></param>
        /// <param name="endDate"></param>
        /// <returns></returns>
        public ExchangeRate GetRate(String fromCurrency, String toCurrency, DateTime date, 
            bool searchAfterDate = false)
        {
            if (fromCurrency == toCurrency)
            {
                return new ExchangeRate(DateTime.Now, fromCurrency, toCurrency, 1, "same currency");
            }

            if (_table == null) { return null; }

            var key = new Tuple<string, string>(fromCurrency, toCurrency);
            var ratesByDate = _table[key];

            // Assumes list is sorted by date, increasing.

            // Search before the date (default)
            ExchangeRate rate = ratesByDate.LastOrDefault(x => x.Date <= date);
            if (rate != null) { return rate; }

            // Optionally search after the date
            if (searchAfterDate)
            {
                rate = ratesByDate.FirstOrDefault(x => x.Date >= date);
                if (rate != null) { return rate; }
            }

            return rate;
        }

        public string ToDisplayString()
        {
            return _table.ToDisplayString();
        }

        /// <summary>
        /// Converts the exchange rate table to a sorted list of rates
        /// </summary>
        /// <returns></returns>
        public IEnumerable<ExchangeRate> ToSortedList()
        {
            if (_table == null) { return new List<ExchangeRate>(); }
            return ToSortedList(_table);
        }

        static IEnumerable<ExchangeRate> ToSortedList(
            ILookup<Tuple<string, string>, ExchangeRate> lookupTable)
        {
            return lookupTable
                .SelectMany(x => lookupTable[x.Key]) // Flatten the lists from mapping table
                .OrderBy(x => x.FromCurrency).ThenBy(x => x.ToCurrency).ThenBy(x => x.Date); // Sort the elements
        }

        static ILookup<Tuple<string, string>, ExchangeRate> ToLookupTable
            (IEnumerable<ExchangeRate> rates)
        {
            return rates
                .OrderBy(x => x.Date)
                .ToLookup(x => new Tuple<string, string>(x.FromCurrency, x.ToCurrency));
        }
    }

}
